//============================================================================
// Name        : indexMain.cpp
// Author      : lucmir
// Description :
//============================================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <cstdlib>
#include <iostream>

#include "htmlcxx/html/ParserDom.h"
#include "htmlcxx/html/Node.h"
#include "CollectionReader.h"

#include "Run.h"
#include "Lexon.h"
#include "Parser.h"
#include "IndexBuilder.h"
#include "TimeMeasurement.h"
#include "DocumentInfo.h"
#include "DocumentIndex.h"
#include "AnchorText.h"

using namespace std;
using namespace RICPNS;
using namespace htmlcxx;

bool getArgs(int argc, char** argv, char **inputDir, char **inputIndexFileName,
		char **tmpDir, char **indexFileName, char **vocabularyFileName,
		char **documentsFileName, char **docLinksFileName, char **anchorTextsFileName);


int main(int argc, char** argv) {

	// Get parameters
	char *inputDir = NULL, *inputIndexFileName = NULL, *indexFileName = NULL,
		*tmpDir = NULL, *vocabularyFileName = NULL, *documentsFileName = NULL,
		*docLinksFileName = NULL, *anchorTextsFileName = NULL;
	if (!getArgs(argc, argv, &inputDir, &inputIndexFileName, &tmpDir,
			&indexFileName, &vocabularyFileName, &documentsFileName,
			&docLinksFileName, &anchorTextsFileName)) {
		return EXIT_SUCCESS;
	}

	std::cout << "\n>> PROGRAMA PARA GERACAO DE INDICE <<\n";

	// Start time measurement
	TimeMeasurement tm;
	tm.setUsrTime();
	tm.setSysTime();
	tm.setTotalTime();

	// Initialize the parser
	IndexBuilder *indexBuilder = new IndexBuilder(tmpDir);
	Parser *parser = new Parser(std::string(inputDir), std::string(inputIndexFileName));

	// Read the list of document urls
	std::ostringstream indexFileNamePath;
	indexFileNamePath << inputDir << "/" << inputIndexFileName;
	bool result = indexBuilder->getDocIdx()->readUrls(indexFileNamePath.str().c_str());
	if(!result) {
		return EXIT_FAILURE;
	}

	// Initialize AnchorText
	AnchorText *anchorText = new AnchorText();

	// Open docLinks for reading
	std::map<std::string, unsigned int>::iterator itr;
	std::cout << '\n' << "Iniciando leitura dos documentos...\t[  OK  ]\n";
	std::ofstream docLinksFile;
	docLinksFile.open(docLinksFileName);
	if(!docLinksFile.is_open()) {
		std::cout << "\nERRO: Não foi possível abrir o arquivo: " << docLinksFileName << "\n";
		return EXIT_FAILURE;
	}

	// For each document
	unsigned int count = 0;
	while(parser->getNextDocument()) {
		parser->parseDocument(indexBuilder->getRun(), indexBuilder->getLexon(),
			indexBuilder->getDocIdx(), &docLinksFile, anchorText);
		indexBuilder->manageRuns(indexBuilder->getLexon());
		std::cout << '\r' << "Documentos lidos: " << ++count;
		std::flush(std::cout);
	}
	docLinksFile.close();

	// Write runs to file (flush the buffer of registries)
	indexBuilder->writeRunToFile(indexBuilder->getLexon());

	// Generate index
	std::cout << "\t\t\t[  OK  ]\nVocabulário contém: " << indexBuilder->getLexon()->getLen() << " termos";
	std::cout << "\t\t[  OK  ]\nIniciando construção do índice... ";
	result = indexBuilder->getIndex()->openToWrite( std::string(indexFileName) );
	if(!result) {
		return EXIT_FAILURE;
	}
	result = indexBuilder->buildIndex();
	if(!result) {
		return EXIT_FAILURE;
	}
	indexBuilder->getIndex()->closeToWrite();

	// Print vocabulary
	std::cout << "\t[  OK  ]\nSalvando vocabulário...";
	result = indexBuilder->getLexon()->printToFile( std::string(vocabularyFileName) );
	if(!result) {
		return EXIT_FAILURE;
	}

	// Print documents informations
	std::cout << "\t\t\t[  OK  ]\nSalvando informações dos documentos...";
	result = indexBuilder->getDocIdx()->printToFile( std::string(documentsFileName) );
	if (!result) {
		return EXIT_FAILURE;
	}

	// Print anchor text
	anchorText->printToFile(anchorTextsFileName);

	// Finish time measurement
	tm.setUsrTime();
	tm.setSysTime();
	tm.setTotalTime();

	std::cout << "\t[  OK  ]\n\nÍndice criado com sucesso!!!\n";
	tm.print();
}

void usage() {
	std::cout << "\n\tPrograma para geração do arquivo-invertido.\n\n"
		<< "\tParâmetros:\n"
		<< "\t\t-i <entrada - índice de documentos>\n"
		<< "\t\t-f <entrada - diretório com documentos>\n"
		<< "\t\t-t <saída - diretório para arquivos temporários>\n"
		<< "\t\t-a <saída - arquivo-invertido gerado>\n"
		<< "\t\t-v <saída - vocabulário>\n"
		<< "\t\t-d <saída - documentos>\n"
		<< "\t\t-p <saída - links entre documentos>\n"
		<< "\t\t-l <saída - textos âncoras>\n\n";
}

bool getArgs(int argc, char** argv, char **inputDir, char **inputIndexFileName,
		char **tmpDir, char **indexFileName, char **vocabularyFileName,
		char **documentsFileName, char **docLinksFileName,  char **anchorTextsFileName) {
	int opt;
	while((opt = getopt(argc,argv,"i:f:t:a:v:d:l:p:h"))!=-1) {
		switch(opt) {
			case 'h':
				usage();
				return false;
			case 'i':
				*inputIndexFileName = optarg;
				break;
			case 'f':
				*inputDir = optarg;
				break;
			case 't':
				*tmpDir = optarg;
				break;
			case 'a':
				*indexFileName = optarg;
				break;
			case 'v':
				*vocabularyFileName = optarg;
				break;
			case 'd':
				*documentsFileName = optarg;
				break;
			case 'p':
				*docLinksFileName = optarg;
				break;
			case 'l':
				*anchorTextsFileName = optarg;
				break;
			default:
				usage();
				return false;
		}
	}
	if (*inputDir == NULL || *inputIndexFileName == NULL || *tmpDir == NULL
			|| *indexFileName == NULL || *vocabularyFileName == NULL
			|| *documentsFileName == NULL || *docLinksFileName == NULL
			|| *anchorTextsFileName == NULL) {
		usage();
		return false;
	}
	return true;
}
